/*
* Copyright (C) 2014 MediaTek Inc.
* Modification based on code covered by the mentioned copyright
* and/or permission notice(s).
*/
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.contacts.vcard;

import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.RawContactsEntity;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.android.vcard.VCardComposer;
import com.android.vcard.VCardConfig;
import com.duoqin.syncassistant.R;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

/**
 * Class for processing one export request from a user. Dropped after exporting requested Uri(s).
 * will create another object when there is another export request.
 */
public class ExportProcessor extends ProcessorBase {
    private static final String LOG_TAG = "duoqin_ExportProcessor";
    private static final boolean DEBUG = true;

    public static final String INTENT_ACTION_VCARD = "vcard";
    public static final String INTENT_EXTRA_VCARD_PATH = "vcard_file";
    public static final String VCARD_FILE_NAME = "vcard.vcf";

    public final ArrayList<String> mSelectedContacts;

    private final Context mContext;
    private final ContentResolver mResolver;
    private final NotificationManager mNotificationManager;

    private volatile boolean mCanceled;
    private volatile boolean mDone;
    /** M:[MTK IMPORT/EXPORT] handle cancel in the cancel function */
    private volatile boolean mIsRunning = false;
    /** M:[MTK IMPORT/EXPORT]  New Feature All of process should not be stopped by PM. */
    private PowerManager.WakeLock mWakeLock;

    public ExportProcessor(Context context, ArrayList<String> selectedContacts) {
        mContext = context.getApplicationContext();
        mResolver = mContext.getContentResolver();
        mNotificationManager =
                (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        mSelectedContacts = selectedContacts;

        /** M: [MTK IMPORT/EXPORT]all of process should not be stopped by PM. @{ */
        final PowerManager powerManager = (PowerManager) mContext.getApplicationContext()
                .getSystemService(Context.POWER_SERVICE);
        mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
                | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
        /** @} */
    }

    @Override
    public void run() {
        /** M: [MTK IMPORT/EXPORT] handle cancel in the cancel function */
        mIsRunning = true;
        /** M: New Feature, all of process should not be stopped by PM.*/
        mWakeLock.acquire();

        // ExecutorService ignores RuntimeException, so we need to show it here.
        try {
            runInternal();

            if (isCancelled()) {
                doCancelNotification();
            }
        } catch (OutOfMemoryError|RuntimeException e) {
            throw e;
        } finally {
            synchronized (this) {
                mDone = true;
            }
            /** M: [MTK IMPORT/EXPORT] all of process should not be stopped by PM. @{*/
            if (mWakeLock != null && mWakeLock.isHeld()) {
                mWakeLock.release();
            }
            /** @} */
        }
    }

    private void runInternal() {
        if (DEBUG) Log.d(LOG_TAG, String.format("vCard export has started."));
        if (mSelectedContacts == null || mSelectedContacts.size() == 0) return;
        StringBuilder mVCardStringBuilder = new StringBuilder();
        VCardComposer composer = null;
        boolean successful = false;
        File file = null;
        try {
            if (isCancelled()) {
                Log.i(LOG_TAG, "Export request is cancelled before handling the request");
                return;
            }

            final int vcardType = VCardConfig.getVCardTypeFromString(
                    mContext.getString(R.string.config_export_vcard_type));

            composer = new VCardComposer(mContext, vcardType, true);

            // for test
            // int vcardType = (VCardConfig.VCARD_TYPE_V21_GENERIC |
            //     VCardConfig.FLAG_USE_QP_TO_PRIMARY_PROPERTIES);
            // composer = new VCardComposer(ExportVCardActivity.this, vcardType, true);

            final Uri contentUriForRawContactsEntity = RawContactsEntity.CONTENT_URI;

            StringBuilder sb=new StringBuilder();
            boolean first=true;
            sb.append("(");
            for (String key:mSelectedContacts) {
                if (!first) {
                    sb.append(",");
                }
                first=false;
                sb.append("\"");
                sb.append(key);
                sb.append("\"");
            }
            sb.append(")");

            /** M: [MTK IMPORT/EXPORT] @{
             * Description: The multiple contacts could be selected to export to SD card.
             */
            if (!composer.init(Contacts.CONTENT_URI, new String[] {Contacts._ID},
                    Contacts._ID+" in "+sb.toString(), null, null, contentUriForRawContactsEntity)) {
            /** @} */
                final String errorReason = composer.getErrorReason();
                Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
                final String translatedErrorReason =
                        translateComposerError(errorReason);
                final String title =
                        mContext.getString(R.string.fail_reason_could_not_initialize_exporter,
                                translatedErrorReason);
                doFinishNotification(title, null);
                return;
            }

            final int total = composer.getCount();
            if (total == 0) {
                final String title =
                        mContext.getString(R.string.fail_reason_no_exportable_contact);
                doFinishNotification(title, null);
                return;
            }

            int current = 1;  // 1-origin
            while (!composer.isAfterLast()) {
                if (isCancelled()) {
                    Log.i(LOG_TAG, "Export request is cancelled during composing vCard");
                    return;
                }
                mVCardStringBuilder.append(composer.createOneEntry());

                // vCard export is quite fast (compared to import), and frequent notifications
                // bother notification bar too much.
                if (current % 100 == 1) {
                    doProgressNotification(null, total, current);
                }
                current++;
            }

            file = new File(mContext.getCacheDir(), VCARD_FILE_NAME);
            if (file.exists()) {
                file.delete();
            }
            FileOutputStream fos = new FileOutputStream(file);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos));
            writer.write(mVCardStringBuilder.toString());
            writer.close();
            fos.close();
            successful = true;

            doFinishNotification("export success", null);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Intent exportVcardIntent = new Intent(INTENT_ACTION_VCARD);
            if (file != null) {
                exportVcardIntent.putExtra(INTENT_EXTRA_VCARD_PATH, file.getAbsolutePath());
            }
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(exportVcardIntent);
            Log.i(LOG_TAG, "finished exporting vCard " + mVCardStringBuilder.length());

            if (composer != null) {
                composer.terminate();
            }
        }
    }

    private String translateComposerError(String errorMessage) {
        final Resources resources = mContext.getResources();
        if (VCardComposer.FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO.equals(errorMessage)) {
            return resources.getString(R.string.composer_failed_to_get_database_infomation);
        } else if (VCardComposer.FAILURE_REASON_NO_ENTRY.equals(errorMessage)) {
            return resources.getString(R.string.composer_has_no_exportable_contact);
        } else if (VCardComposer.FAILURE_REASON_NOT_INITIALIZED.equals(errorMessage)) {
            return resources.getString(R.string.composer_not_initialized);
        } else {
            /// M: [ALPS00413434]
            return resources.getString(R.string.vcard_generic_failure);
            /// @}
        }
    }

    private void doProgressNotification(Uri uri, int totalCount, int currentCount) {
    }

    private void doCancelNotification() {
        if (DEBUG) Log.d(LOG_TAG, "send cancel notification");
    }

    private void doFinishNotification(final String title, final String description) {
        if (DEBUG) Log.d(LOG_TAG, "send finish notification: " + title + ", " + description);
    }

    /**
     * Pass intent with ACTION_SEND to notification so that user can press the notification to
     * share contacts.
     */
    private void doFinishNotificationWithShareAction(final String title, final String
            description, Uri uri) {
    }

    @Override
    public synchronized boolean cancel(boolean mayInterruptIfRunning) {
        if (DEBUG) Log.d(LOG_TAG, "received cancel request");
        if (mDone || mCanceled) {
            return false;
        }
        mCanceled = true;
        /** M: [MTK IMPORT/EXPORT] Change Feature @{
         *  Descriptions: handle cancel in the cancel function
         */
        if (!mIsRunning) {
            doCancelNotification();
        }
        /** @} */
        return true;
    }

    @Override
    public synchronized boolean isCancelled() {
        return mCanceled;
    }

    @Override
    public synchronized boolean isDone() {
        return mDone;
    }

}
